/**
  ETFDAQ Project
  \class TACrate
  \brief A VME Crate holding a controller, and multiple plugin modules
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-08-12
  \date 2024-08-12 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include "TACrate.h"
#include "TAController.h"
#include "TACard.h"
#include "TAv785.h"
#include "TAv785N.h"
#include "TAv792N.h"
#include "TAv830.h"
#include "TAv895.h"
#include "TAv965.h"
#include "TAv1290A.h"
#include "TAmADC32.h"
#include "TAmTDC32.h"
#include "TAMsg.h"
#include "TAMath.h"
#include "TAYaml.h"
#include "card_type.h"
#include "event.h"

#define er TAMsg::Error

TACrate::TACrate(TAController *ctl, const TAYaml *cfg, int id) : fId(id),
fController(ctl), fCards{0}, fConfig(cfg), fDebug(0){
  if(!ctl) er("TACrate", "ctor: nullptr controller");
} // end ctor

TACrate::~TACrate(){
  for(auto &c : fCards) delete c;
} // end dtor

void TACrate::Initialize(){
  // add cards according to user input file //
  const TAYaml &cfg = (*fConfig)["card"];
  if(!cfg) er("TACrate", "Initialize: \'card\' in yaml configfile not defined");

  int cnt = 0;
  for(auto &c : cfg) if(c["crate"].as<int>() == fId){
    AddCard(c);
    cnt++;
  } // end for
  if(!cnt) er("TACrate", "Initialize: no card is registered in crate %d", fId);
} // end member function Initialize

void TACrate::SetDebug(bool opt){
  fDebug = opt;
} // end member function SetDebug

void TACrate::AddCard(const TAYaml &c){
  string ct = c["type"].as<string>();
  ECardType etype = ECardType::UDF;
  for(const auto &p : gCardMap)
    if(TAMath::istrcmp(ct, p.first)) etype = p.second; // case insensitive
  if(ECardType::UDF == etype)
    er("TACrate", "AddCard: unknown card type: %s.", ct.data());
  auto ctl = fController; // just for convenience
  switch(etype){
    case ECardType::v785: AddCard(new TAv785(ctl, c)); break;
    case ECardType::v785N: AddCard(new TAv785N(ctl, c)); break;
    case ECardType::v792N: AddCard(new TAv792N(ctl, c)); break;
    case ECardType::v830: AddCard(new TAv830(ctl, c)); break;
    case ECardType::v895: AddCard(new TAv895(ctl, c)); break;
    case ECardType::v965: AddCard(new TAv965(ctl, c)); break;
    case ECardType::v1290A: AddCard(new TAv1290A(ctl, c)); break;
    case ECardType::mADC32: AddCard(new TAmADC32(ctl, c)); break;
    case ECardType::mTDC32: AddCard(new TAmTDC32(ctl, c)); break;
    default:
      TAMsg::Warn("TACrate", "AddCard: unprepared card type: %s", ct.data());
      break;
  } // end switch
} // end member function AddCard

void TACrate::AddCard(TACard *c){
  if(!c) er("TACrate", "AddCard: nullptr controller");
  c->SetDebug(fDebug);
  c->Initialize();
  fCards[c->GetSlot() - 1] = c;
} // end member function AddCard

void TACrate::ClearData(){
  for(auto &c : fCards) if(c) c->ClearData();
} // end member function ClearData

void TACrate::ResetForNewRun(){
  for(auto &c : fCards) if(c) c->ResetForNewRun();
} // end member function ResetForNewRun

// SR for all the cards
void TACrate::ClearEventCounter(){
  for(auto &c : fCards) if(c) c->ClearEventCounter();
} // end member function ClearEventCounter

// module buffer => controller buffer
int TACrate::ReadEvent(event_t *ev){
  if(fDebug){
    cout << "\n\033[33;1m____________ Crate";
    cout << fId << " ReadEvent ("  << ev->index;
    cout << ") Print ___________\033[0m" << endl;
  } // end if
  // read the event from card buffer to the local event buffer //
  for(auto &c : fCards) if(c){
    if(c->DataReady()){
      ev->nFired++; // nof fired cards
      const int ng = c->ReadData(ev->writePtr());
      ev->length += ng;
      if(fDebug){
        c->TellYouWhoIAm();
        cout << "nOfWordsGet: " << ng << ", ev_len: " << ev->length << endl;
      } // end if
      if(ev->length > MAXEVLEN){
        TAMsg::Warn("TACrate", "ReadEvent: ev_len too long: %d, "
          "ignore the whole event (not only this card: %s, slot %d).",
          ev->length, c->GetName(), c->GetSlot());
        ev->init(); // empty the event (length=0, index+1)
        return -1;
      } // end if
    } // end if
  } // end outer for
  // if(ev->length != 186){ // DEBUG
  //   TAMsg::Warn("TACrate", "ReadEvent: abnormal ev_len: %d", ev->length);
  //   // getchar();
  // } // end if

  if(fDebug){
    cout << "\033[33;1m____________ event Length: ";
    cout << ev->length << " words ___________\n\033[0m" << endl;
  } // end if
  return 0;
} // end member function ReadEvent

TACard *TACrate::GetCard(int slot) const{
  if(slot <= 1 || slot > NSLOT) er("TACrate", "GetCard: illegal slot=%d", slot);
  return fCards[slot - 1];
} // end member function GetCard

void TACrate::Print() const{
  int cnt = 0;
  cout << "\n\033[34;1m------ Crate " << fId;
  cout << " Information BEGIN ------\033[0m";
  for(auto c : fCards) if(c){ c->Print(); cnt++; }
  cout << "\n--- Totally " << cnt << " cards are used in crate ";
  cout << fId << " ---" << endl;
  cout << "\033[34;1m------- Crate " << fId;
  cout << " Information END -------\033[0m\n" << endl;
} // end member function Print
